package cn.redsoft.wechatapi.common.util;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.redsoft.wechatapi.common.Consts;
import cn.redsoft.wechatapi.common.exception.ConfigParseErrorException;
import cn.redsoft.wechatapi.handler.RequestMessageHandler;
import cn.redsoft.wechatapi.handler.RequestMsgHandlerInvoker;
import cn.redsoft.wechatapi.msg.req.RequestMsg;

public class ConfigParser {
	private Logger log = LoggerFactory.getLogger(ConfigParser.class);

	private List<Class<? extends RequestMessageHandler<? extends RequestMsg>>> handlers = new ArrayList<Class<? extends RequestMessageHandler<? extends RequestMsg>>>();

	public ConfigParser() {
	}

	public void parse(String xmlConfigPath) throws DocumentException,
			ConfigParseErrorException, ClassNotFoundException {
		SAXReader reader = new SAXReader();
		InputStream in = ConfigParser.class.getClassLoader()
				.getResourceAsStream(xmlConfigPath);
		Document doc = reader.read(in);
		Element root = doc.getRootElement();
		// 1. 解析常量配置
		this.parseConstFromRoot(root);

		// 2. 解析Handler配置

		this.parseHandlersFromRoot(root);

		try {
			in.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void parse() throws DocumentException, ConfigParseErrorException,
			ClassNotFoundException {
		this.parse("WechatConfig.xml");
	}

	public List<Class<? extends RequestMessageHandler<? extends RequestMsg>>> getHandlers() {
		return handlers;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	protected void parseHandlersFromRoot(Element root)
			throws ClassNotFoundException {
		log.info("Handler 配置解析开始 .");
		String prefixString = "";
		Attribute prefix = root.element("handlers").attribute("prefix");
		if (prefix != null) {
			String text = prefix.getText();
			if (text != null && !"".equals(text))
				prefixString = text;
		}
		Iterator iterator = root.element("handlers").elements("handler")
				.iterator();
		while (iterator.hasNext()) {
			Element e = (Element) iterator.next();
			this.parseClassOfHandler(prefixString, e);
		}

		log.info("Handler 配置解析完毕 .");
		log.info("Handler 执行顺序 :");
		for (int i = 0; i < handlers.size(); i++) {
			Class<? extends RequestMessageHandler<? extends RequestMsg>> c = handlers
					.get(i);
			Type type = c.getGenericInterfaces()[0];
			ParameterizedType parameterizedType = (ParameterizedType) type;
			Type actualType = parameterizedType.getActualTypeArguments()[0];
			Class<? extends RequestMsg> msgCls = (Class<? extends RequestMsg>) actualType;
			RequestMsgHandlerInvoker.handlers.put(msgCls, c);
			log.info("\t{}. {}<-->{} ({})", i + 1, msgCls.getSimpleName(),
					c.getSimpleName(), c.getName());
		}
		log.info("Handler 配置已成功映射到 {} .",
				RequestMsgHandlerInvoker.class.getName());
		log.info("--------------------------------------------");

	}

	@SuppressWarnings("unchecked")
	private void parseClassOfHandler(String prefixString, Element e)
			throws ClassNotFoundException {
		boolean interfaceImled = false;
		Attribute nameAttr = e.attribute("name");
		if (nameAttr == null)
			throw new ConfigParseErrorException("handler 解析错误 : 缺少 name 属性 .");
		if (nameAttr.getText() == null || nameAttr.getText().equals(""))
			throw new ConfigParseErrorException(
					"handler 解析错误 : handler 的 name 属性值为空 .");
		String className = prefixString + "." + nameAttr.getText();
		Class<?> handlerClass = Class.forName(className.trim());
		Class<?>[] interfaces = handlerClass.getInterfaces();
		if (interfaces.length == 0) {
			throw new ConfigParseErrorException("handler 解析错误 : "
					+ handlerClass + " 必须实现 "
					+ RequestMessageHandler.class.getName() + " 接口 .");
		} else if (interfaces.length == 1) {
			if (interfaces[0] != RequestMessageHandler.class)
				throw new ConfigParseErrorException("handler 解析错误 : "
						+ handlerClass + " 必须实现 "
						+ RequestMessageHandler.class.getName() + " 接口 .");
		} else {
			for (int i = 0; i < interfaces.length; i++) {
				if (interfaces[i] == RequestMessageHandler.class) {
					interfaceImled = true;
				}
			}
			if (!interfaceImled) {
				throw new ConfigParseErrorException("handler 解析错误 : "
						+ handlerClass + " 必须实现 "
						+ RequestMessageHandler.class.getName() + " 接口 .");
			}
		}

		this.handlers
				.add((Class<RequestMessageHandler<? extends RequestMsg>>) handlerClass);
	}

	@SuppressWarnings("rawtypes")
	protected void parseConstFromRoot(Element rootElement)
			throws ConfigParseErrorException {
		log.info("---------------------------");
		log.info("常量配置解析开始 .");
		Map<String, String> consts = new HashMap<String, String>();
		Iterator iterator = rootElement.element("constants")
				.elements("constant").iterator();
		while (iterator.hasNext()) {
			Element e = (Element) iterator.next();
			fillConstMap(consts, e);
		}
		log.info("常量配置解析完毕 .");
		initConsts(consts);
		log.info("常量配置已成功映射到 {} ", Consts.class.getName());
		log.info("---------------------------");
	}

	private void initConsts(Map<String, String> consts) {
		String charset = consts.get("wechat.charset");
		if (charset != null) {
			Consts.CHARACTER_ENCODING = charset;
			log.info("\twechat.charset = {} .", charset);
		} else {
			log.info("\twechat.charset = {} (默认配置) .",
					Consts.CHARACTER_ENCODING);
		}
		String token = consts.get("wechat.token");
		if (token != null) {
			log.info("\twechat.token = {} .", token);
			Consts.TOKEN = token;
		} else {
			log.info("\twechat.token = {} (默认配置) .", Consts.TOKEN);
		}
	}

	private void fillConstMap(Map<String, String> map, Element e) {
		Attribute keyAttr = e.attribute("name");
		if (keyAttr == null)
			throw new ConfigParseErrorException("常量配置错误:缺少 name 属性.");
		String keyValue = keyAttr.getText();
		if (keyValue == null || "".equals(keyValue))
			throw new ConfigParseErrorException("常量配置错误:name 属性不能为空.");

		Attribute valueAttr = e.attribute("value");
		if (valueAttr == null)
			throw new ConfigParseErrorException("常量配置错误:缺少 value 属性.");
		String valueValue = (valueAttr.getText() == null || "".equals(valueAttr
				.getText())) ? null : valueAttr.getText().trim();
		map.put(keyValue, valueValue);
	}
}
